home *** CD-ROM | disk | FTP | other *** search
Text File | 1996-07-05 | 11.6 KB | 371 lines | [TEXT/PJMM] |
- {This program is based on Finder Marquee by Jordan Zimmerman.}
- {}
- {Using the techniques in the original C program, this program is }
- {done in Pascal. It uses a linked lists for objects instead of}
- {an array, and a new function was added for adding objects (AddSquare).}
- {Some of the other data structures were also changed.}
- {}
- {This code implements a "rubber band" marquee select rect}
- {with very smooth drawing in a manner similar to the Mac Finder.}
- { }
- {Project includes:}
- {}
- {Marquee.p - Pascal source code}
- {Marquee.µ - Codewarrior Pascal project file.}
- { }
- {8/13/95 - Bill Catambay, catambay@aol.com}
-
- {This version is modified by Ingemar R.}
- {- The program no longer messes with the font and size of the Window Mgr port.}
- {- Works with Think Pascal; added Marquee.π, the Think Pascal project file.}
- {- Changed routine names and some variable names to get closer to the standard.}
- {- The routine AddSquare disposed the memory it allocated, which could cause some}
- {rather violent crashes under Think Pascal. (I don't know why it worked with MWP -}
- {probably due to some differences in the way New is implemented.)}
-
- program Marquee;
-
- {$IFC UNDEFINED THINK_PASCAL}
- uses
- Types, Windows, Events, Fonts, Dialogs, TextUtils;
- {$ENDC}
-
- type
- FinderMarqueeRec = record
- marquee_r: Rect; { current marquee_r }
- pin_pt: Point; { mouse down point }
- current_pt: Point; { current mouse location }
- end;
- MarqueePtrType = ^FinderMarqueeRec;
- HighlightPtr = ^HighlightRec;
- HighlightRec = record
- bounds_r: Rect;
- selected_flag: boolean;
- next_rec: HighlightPtr;
- end;
-
- var
- bounds_r: Rect;
- mainw: WindowPtr;
- statusw: WindowPtr;
- is_done: Boolean;
- the_event: EventRecord;
- menu: MenuHandle; {menuRef;}
- font_num: Integer;
- tab_ptr: HighlightPtr;
-
- function SelectionsProc (marquee_ptr: MarqueePtrType): boolean;
- var
- i, qty: Integer;
- sect_r: Rect;
- tmp: HighlightPtr;
- begin
- tmp := tab_ptr;
- while tmp <> nil do
- begin
- if SectRect(marquee_ptr^.marquee_r, tmp^.bounds_r, sect_r) <> tmp^.selected_flag then
- begin
- SelectionsProc := TRUE;
- exit(SelectionsProc);
- end;
- tmp := tmp^.next_rec;
- end;
- SelectionsProc := false;
- end; {SelectionsProc}
-
- { invert any of the highlight rects that intersect the current marquee rect }
- procedure ChangeSelectionProc (marquee_ptr: MarqueePtrType; old_marquee_r: Rect);
- var
- i, qty: Integer;
- tmp: HighlightPtr;
- sect_r: Rect;
- begin
- tmp := tab_ptr;
- while tmp <> nil do
- begin
- if SectRect(marquee_ptr^.marquee_r, tmp^.bounds_r, sect_r) <> tmp^.selected_flag then
- begin
- tmp^.selected_flag := not tmp^.selected_flag;
- InvertRect(tmp^.bounds_r);
- end;
- tmp := tmp^.next_rec;
- end;
- end; {ChangeSelectionProc}
-
- function Num2Str (num: integer): string;
- var
- str: str255;
- begin
- NumToString(num, str);
- Num2Str := str;
- end; { of Num2Str }
-
- { outline the marquee rect in Xor gray }
- procedure DrawMarqueeR (marquee_ptr: MarqueePtrType);
- var
- pen_state: PenState;
- begin
- GetPenState(pen_state);
- PenMode(patXor);
- {$IFC UNDEFINED THINK_PASCAL}
- PenPat(qd.gray);
- {$ELSEC}
- PenPat(gray);
- {$ENDC}
- FrameRect(marquee_ptr^.marquee_r);
- SetPenState(pen_state);
- end; {DrawMarqueeR}
-
- { calculating the marquee rect isn't as simple as Pt2Rect. Using Pt2Rect }
- { causes the pin point to shift around. This function will calculate a }
- { correct marquee rect that keeps the pin point in place }
- procedure CalculateMarqueeR (marquee_ptr: MarqueePtrType);
- begin
- if (marquee_ptr^.current_pt.h >= marquee_ptr^.pin_pt.h) & (marquee_ptr^.current_pt.v >= marquee_ptr^.pin_pt.v) then { Quadrant IV }
- SetRect(marquee_ptr^.marquee_r, marquee_ptr^.pin_pt.h, marquee_ptr^.pin_pt.v, marquee_ptr^.current_pt.h + 1, marquee_ptr^.current_pt.v + 1)
- else if (marquee_ptr^.current_pt.h <= marquee_ptr^.pin_pt.h) & (marquee_ptr^.current_pt.v <= marquee_ptr^.pin_pt.v) then { Quadrant I }
- SetRect(marquee_ptr^.marquee_r, marquee_ptr^.current_pt.h, marquee_ptr^.current_pt.v, marquee_ptr^.pin_pt.h + 1, marquee_ptr^.pin_pt.v + 1)
- else if (marquee_ptr^.current_pt.h >= marquee_ptr^.pin_pt.h) & (marquee_ptr^.current_pt.v <= marquee_ptr^.pin_pt.v) then { Quadrant II }
- SetRect(marquee_ptr^.marquee_r, marquee_ptr^.pin_pt.h, marquee_ptr^.current_pt.v, marquee_ptr^.current_pt.h + 1, marquee_ptr^.pin_pt.v + 1)
- else { Quadrant III }
- SetRect(marquee_ptr^.marquee_r, marquee_ptr^.current_pt.h, marquee_ptr^.pin_pt.v, marquee_ptr^.pin_pt.h + 1, marquee_ptr^.current_pt.v + 1);
- end; {CalculateMarqueeR}
-
- procedure FinderMarqueeBegin (marquee_ptr: MarqueePtrType; mouse_down_pt: Point);
- var
- old_marquee_r: rect;
- begin
- marquee_ptr^.pin_pt := mouse_down_pt;
- marquee_ptr^.current_pt := mouse_down_pt;
- CalculateMarqueeR(marquee_ptr);
- ChangeSelectionProc(marquee_ptr, marquee_ptr^.marquee_r);
- DrawMarqueeR(marquee_ptr);
- end; {FinderMarqueeBegin}
-
- { utility to make a 1 pixel thick region of the frame outline of a rect }
- procedure MakeFrameRegion (target_rgn_h: RgnHandle; frame_r: Rect; work_rgn_h: RgnHandle);
- begin
- RectRgn(target_rgn_h, frame_r);
- CopyRgn(target_rgn_h, work_rgn_h);
- InsetRgn(work_rgn_h, 1, 1);
- DiffRgn(target_rgn_h, work_rgn_h, target_rgn_h);
- end; {MakeFrameRegion}
-
- procedure FinderMarqueeContinue (marquee_ptr: MarqueePtrType; new_pt: Point);
- var
- pen_state: PenState;
- old_marquee_r: Rect;
- old_rgn_h: RgnHandle;
- work_rgn_h: RgnHandle;
- new_rgn_h: RgnHandle;
- clip_rgn_h: RgnHandle;
- success_flag: boolean;
- begin
- old_rgn_h := nil;
- work_rgn_h := nil;
- new_rgn_h := nil;
- clip_rgn_h := nil;
- success_flag := false;
- { avoid flashing step 1 - do nothing if the mouse hasn't moved }
- if EqualPt(new_pt, marquee_ptr^.current_pt) then
- exit(FinderMarqueeContinue);
- clip_rgn_h := NewRgn;
- if clip_rgn_h = nil then
- exit(FinderMarqueeContinue); { can't find 10 bytes! We're probably already in big trouble }
- { we'll be messing with the clip, so save it for later restoration }
- GetClip(clip_rgn_h);
- GetPenState(pen_state);
- PenMode(patXor);
- {$IFC UNDEFINED THINK_PASCAL}
- PenPat(qd.gray);
- {$ELSEC}
- PenPat(gray);
- {$ENDC}
- { save the old marquee_r and setup the new one }
- old_marquee_r := marquee_ptr^.marquee_r;
- marquee_ptr^.current_pt := new_pt;
- CalculateMarqueeR(marquee_ptr);
- repeat
- old_rgn_h := NewRgn;
- if old_rgn_h = nil then
- Leave;
- work_rgn_h := NewRgn;
- if work_rgn_h = nil then
- Leave;
- new_rgn_h := NewRgn;
- if new_rgn_h = nil then
- Leave;
- { generate 1 pixel thick outline regions of the old and new marquee_r }
- MakeFrameRegion(old_rgn_h, old_marquee_r, work_rgn_h);
- MakeFrameRegion(new_rgn_h, marquee_ptr^.marquee_r, work_rgn_h);
- { get the area in common between the old and the new }
- SectRgn(old_rgn_h, new_rgn_h, work_rgn_h);
- { set the clip to the old clip minus the common area of the old and new marquee rect }
- DiffRgn(clip_rgn_h, work_rgn_h, work_rgn_h);
- SetClip(work_rgn_h);
- if SelectionsProc(marquee_ptr) then
- begin
- { If there is a selection, the old marquee must be erased, the selections must be drawn, }
- { and then the new marquee can be drawn. }
- FrameRect(old_marquee_r);
- ChangeSelectionProc(marquee_ptr, old_marquee_r);
- FrameRect(marquee_ptr^.marquee_r);
- end
- else
- begin
- { If there's no selection change, the marquee can be drawn in one step }
- { that will erase the old and draw the new. }
- UnionRgn(new_rgn_h, old_rgn_h, work_rgn_h);
- PaintRgn(work_rgn_h);
- end;
- success_flag := true;
- until success_flag; { i.e. do once, set success_flag on exit }
- if not success_flag then
- begin
- { memory is evidently very tight, we'll have to live with flashing }
- FrameRect(old_marquee_r);
- ChangeSelectionProc(marquee_ptr, old_marquee_r);
- FrameRect(marquee_ptr^.marquee_r);
- end;
- SetClip(clip_rgn_h);
- SetPenState(pen_state);
- if old_rgn_h <> nil then
- DisposeRgn(old_rgn_h);
- if work_rgn_h <> nil then
- DisposeRgn(work_rgn_h);
- if new_rgn_h <> nil then
- DisposeRgn(new_rgn_h);
- if clip_rgn_h <> nil then
- DisposeRgn(clip_rgn_h);
- SetPort(statusw);
- EraseRect(statusw^.portRect);
- with statusw^.portRect do
- MoveTo(left + 5, top + 10);
- TextSize(9);
- TextFont(font_num);
- with marquee_ptr^.marquee_r do
- DrawString(concat('Size: ', Num2Str(bottom - top), ', ', Num2Str(right - left)));
- with statusw^.portRect do
- MoveTo(left + 5, top + 24);
- DrawString(concat('Mouse: ', Num2Str(new_pt.h), ', ', Num2Str(new_pt.v)));
- SetPort(mainw);
- end; {FinderMarqueeContinue}
-
- { here's the meat of the program }
- { while this example does it all in one function, }
- { you could just as easily separate the various calls and do it in the background }
- procedure DoMarquee (local_pt: Point);
- var
- marquee: FinderMarqueeRec;
- new_pt: Point;
- begin
- FinderMarqueeBegin(@marquee, local_pt);
- while StillDown do
- with marquee.marquee_r do
- begin
- GetMouse(new_pt);
- FinderMarqueeContinue(@marquee, new_pt);
- end;
- DrawMarqueeR(@marquee);
- end; {DoMarquee}
-
- { walk through our highlight rects and see if any of the selections will change. }
- { the marquee drawing must work slightly differently if there's going to be }
- { a selection change. }
- { If there's no selection change, the marquee can be drawn in one step (PaintRgn) }
- { that will erase the old and draw the new. If there is a selection, though, the }
- { old marquee must be erased, the selections must be drawn, and then the new marquee }
- { can be drawn. }
- { draw in response to an update event }
- procedure DrawHighlights;
- var
- i, qty: integer;
- tmp: HighlightPtr;
- begin
- EraseRect(mainw^.portRect);
- tmp := tab_ptr;
- while tmp <> nil do
- begin
- FrameRect(tmp^.bounds_r);
- if tmp^.selected_flag then
- InvertRect(tmp^.bounds_r);
- tmp := tmp^.next_rec;
- end;
- end; {DrawHighlights}
-
- procedure AddSquare (left, top, right, bottom: integer);
- var
- tmp: HighlightPtr;
- begin
- {new(tmp); <- Questionable - you shouldn't mix "New" and "DisposePtr", right?}
- tmp := HighlightPtr(NewPtr(SizeOf(HighlightRec)));
- SetRect(tmp^.bounds_r, left, top, right, bottom);
- tmp^.selected_flag := false;
- tmp^.next_rec := tab_ptr;
- tab_ptr := tmp;
- {DisposePtr(pointer(tmp)); <- serious bug in old version}
- end; {AddSquare}
-
- (* Standard inits *)
- procedure InitToolbox;
- begin
- {$IFC UNDEFINED THINK_PASCAL}
- MaxApplZone;
- InitGraf(@qd.thePort);
- InitFonts;
- FlushEvents(everyEvent, 0);
- InitWindows;
- InitMenus;
- TEInit;
- InitDialogs(nil);
- {$ENDC}
- InitCursor;
- end; {InitToolbox}
-
-
- begin
- InitToolbox;
- is_done := false;
-
- {In the previous version, the program did the following to set font}
- {and size. NEVER mess with the window mgr port like this! You just}
- {mess up the menus and window titles in all programs, forcing the}
- {user to reboot to get back to normal.}
- {textsize(9);}
- GetFNum('Monaco', font_num);
- {textfont(font_num);}
-
- tab_ptr := nil;
- AddSquare(10, 10, 42, 42);
- AddSquare(50, 75, 82, 107);
- AddSquare(100, 10, 132, 42);
- AddSquare(110, 200, 142, 232);
- AddSquare(308, 308, 340, 340);
- menu := NewMenu(1, 'Finder Marquee');
- InsertMenu(menu, 0);
- DrawMenuBar;
- SetRect(bounds_r, 50, 50, 400, 400);
- mainw := NewCWindow(nil, bounds_r, 'Press Any Key To Quit', true, noGrowDocProc, WindowPtr(-1), false, 0);
- SetRect(bounds_r, 100, 410, 200, 450);
- statusw := NewCWindow(nil, bounds_r, '', true, plaindbox, nil, false, 0);
- SetPort(mainw);
- while not is_done do
- if WaitNextEvent(everyEvent, the_event, GetDblTime, nil) then
- case the_event.what of
- keyDown, autoKey:
- is_done := true;
- updateEvt:
- begin
- BeginUpdate(mainw);
- DrawHighlights;
- EndUpdate(mainw);
- end;
- mouseDown:
- begin
- GlobalToLocal(the_event.where);
- DoMarquee(the_event.where);
- end;
- {CASE}
- end;
- end.
-